// wxWidgets "Hello World" Program

// For compilers that support precompilation, includes "wx/wx.h".
#include <wx/wxprec.h>
#include <wx/filepicker.h>
#include <wx/gdicmn.h>
#include <wx/access.h>
#include <wx/gauge.h>
#include <wx/filename.h>
#include <wx/thread.h>
#include <wx/txtstrm.h>
#include <wx/wfstream.h>
#include <wx/icon.h>

#ifndef WX_PRECOMP
#include <wx/wx.h>
#endif


class MyThread;
WX_DEFINE_ARRAY(MyThread*, ArrayOfMyThread);


class MyApp : public wxApp
{
public:
    virtual bool OnInit();
};

class MyFrame : public wxFrame
{
public:
    MyFrame(wxWindow* parent,
        wxWindowID id,
        const wxString& title,
        const wxPoint& pos = wxDefaultPosition,
        const wxSize& size = wxDefaultSize,
        long style = wxDEFAULT_FRAME_STYLE,
        const wxString& name = wxFrameNameStr);


protected:
    wxCriticalSection rdfile_contents;;
    friend class MyThread;
private:
    void OnHello(wxCommandEvent& event);
    void OnExit(wxCommandEvent& event);
    void OnAbout(wxCommandEvent& event);
    void OnButton(wxCommandEvent& event);

private:
    wxFilePickerCtrl* filePicker;
    wxGauge* MyGauge;
    wxFile* Output_file = new wxFile;

};

class MyThread : public wxThread
{
public:
    MyThread(wxFileOffset offset_begin, wxFileOffset offset_end, 
        MyFrame* handler, MyThread* fa_thread, char* file_string, 
        int deep_num = 0, wxSemaphore* sempaphore = NULL)
        : wxThread(wxTHREAD_DETACHED)
    {
        line_begin = offset_begin;
        line_end = offset_end;
        critical_num = 2 << 20;
        thread_handler = handler;
        filecontent = file_string;
        thread_deep = deep_num + 1;
        father_semaphore = sempaphore;
        Father_Thread = fa_thread;
        my_semaphore = new wxSemaphore(0, 1);
    }


protected:
    virtual ExitCode Entry();

    char* filecontent;
    wxFileOffset line_begin;
    wxFileOffset line_end;
    MyFrame* thread_handler;
    wxSemaphore* my_semaphore;
    wxSemaphore* father_semaphore;
    MyThread* Father_Thread;

private:
    wxFileOffset critical_num;
    int thread_deep;
    wxVector<unsigned char> out_data;
};

enum
{
    ID_Hello = 1,
    ID_Button = 2
};

wxIMPLEMENT_APP(MyApp);

bool MyApp::OnInit()
{
    int width = wxSystemSettings::GetMetric(wxSYS_SCREEN_X, NULL);
    int height = wxSystemSettings::GetMetric(wxSYS_SCREEN_Y, NULL);
    MyFrame* frame = new MyFrame(NULL, wxID_ANY, "Hex2Bin", wxPoint(width / 4, height / 4), 
        wxSize(width / 2, height / 2), wxSYSTEM_MENU | wxMINIMIZE_BOX | wxCLOSE_BOX | wxCAPTION | wxCLIP_CHILDREN);
    frame->Show(true);
    
//    wxBitmap* my_bitmap = new wxBitmap("logo.bmp", wxBITMAP_TYPE_BMP);

//    wxIcon* my_icon = new wxIcon;

//    my_icon->CopyFromBitmap(*my_bitmap);

//    frame->SetIcon(*my_icon);
    return true;
}

MyFrame::MyFrame(wxWindow* parent,
    wxWindowID id,
    const wxString& title,
    const wxPoint& pos,
    const wxSize& size,
    long styleE,
    const wxString& name)
    : wxFrame(parent, id, title, pos, size, styleE, name)
{
    wxMenu* menuFile = new wxMenu;
//    menuFile->AppendSeparator();
    menuFile->Append(wxID_EXIT);



//    wxMenu* menuHelp = new wxMenu;
//    menuHelp->Append(wxID_ABOUT);

    wxMenuBar* menuBar = new wxMenuBar;
    menuBar->Append(menuFile, "&File");
//    menuBar->Append(menuHelp, "&Help");

    SetMenuBar(menuBar);

 //   CreateStatusBar();
 //   SetStatusText("Welcome to wxWidgets!");


//    Bind(wxEVT_MENU, &MyFrame::OnHello, this, ID_Hello);
//    Bind(wxEVT_MENU, &MyFrame::OnAbout, this, wxID_ABOUT);
    Bind(wxEVT_MENU, &MyFrame::OnExit, this, wxID_EXIT);
    Bind(wxEVT_BUTTON, &MyFrame::OnButton, this, ID_Button);

    int width, height;
    GetVirtualSize(&width, &height);


    wxPanel* panel = new wxPanel(this, wxID_ANY);

    filePicker = new wxFilePickerCtrl(panel, wxID_ANY, "hello",
        wxFileSelectorPromptStr, "*.hex", wxPoint(width / 4, height / 2 - 75),
        wxSize(width / 2, 50));

    MyGauge = new wxGauge(panel, wxID_ANY, 100, wxPoint(width / 4, height / 2 + 25), wxSize(width / 2 - 125, 40));

    wxButton* MyButton = new wxButton(panel, ID_Button, "convert",
        wxPoint(3 * width / 4 - 100, height / 2 + 25), wxSize(100, 40));


}

void MyFrame::OnExit(wxCommandEvent& event)
{
    Close(true);
}

//void MyFrame::OnAbout(wxCommandEvent& event)
//{
//    wxMessageBox("This is a wxWidgets Hello World example",
//        "About Hello World", wxOK | wxICON_INFORMATION);
//}
//
//void MyFrame::OnHello(wxCommandEvent& event)
//{
//    wxLogMessage("Hello world from wxWidgets!");
//}

void MyFrame::OnButton(wxCommandEvent& event)
{
    wxFileName MyFileName = filePicker->GetFileName();
    bool diachage = MyFileName.IsFileReadable();
    if (!diachage) {
        wxLogMessage("the file is not exit!");
    }
    else
    {
        wxFileName newFileName = MyFileName;
        wxString myname = MyFileName.GetLongPath();

        newFileName.SetExt(wxString::wxString("bin"));

        wxString newname = newFileName.GetLongPath();

        wxFile* MyInputFile = new wxFile(myname);

        
        Output_file->Create(newname, true);

        wxFileOffset line_begin = 0;
        wxFileOffset line_end = MyInputFile->Length();

 //       wxString* filecontent = new wxString;
        char* filecontent = new char[line_end];
//        filecontent->Alloc(line_end);
        diachage = MyInputFile->Read(filecontent, line_end);

        MyThread* first_thread = new MyThread(line_begin, line_end, this, NULL,filecontent);

        if (first_thread->Run() != wxTHREAD_NO_ERROR)
        {
            wxLogError("Can't create the thread!");
            delete first_thread;
            first_thread = NULL;
        }
        MyInputFile->Close();
        delete(MyInputFile);
    }
}





wxThread::ExitCode MyThread::Entry()
{
    wxString line_data;

    ArrayOfMyThread thread_array;
    wxFileOffset line_mid;
    wxSemaphore* son_semaphore = new wxSemaphore(0, 1);
    int thread_number = 0;
    wxLogDebug("begin");
    int data_num = 0;
    
    thread_handler->MyGauge->SetValue(0);
    while ((line_end - line_begin > critical_num) && (thread_deep <= 2))
    {
        line_mid = (line_begin + line_end) >> 1;
        for (wxFileOffset i = line_mid; i < line_end; i++) {
            if (filecontent[i] == ':') {
                MyThread* son_thread = new MyThread(line_mid, line_end, 
                    thread_handler, this, filecontent, thread_deep++, son_semaphore);

                if (son_thread->Run() != wxTHREAD_NO_ERROR)
                {
                    wxLogError("Can't create the thread!");
                    delete son_thread;
                    son_thread = NULL;
                }

                thread_array.Add(son_thread);
                line_end = line_mid - 1;
                break;
            }
        }
    }
    unsigned char* write_data = new unsigned char[line_end - line_begin];
    for (wxFileOffset i = line_begin; i < line_end; i++)
    {
        if (filecontent[i] == ':')
        {
            long line_len;

            char* stopstring;
            char string[3];
            string[0] = filecontent[i + 7];
            string[1] = filecontent[i + 8];
            string[2] = '\0';
            line_len = strtol(string, &stopstring, 16);
            if (line_len == 0) {

                string[0] = filecontent[i + 1];
                string[1] = filecontent[i + 2];
                string[2] = '\0';
                line_len = strtol(string, &stopstring, 16);


                for (int k = 0; k < line_len; k++)
                {
                    long data;

                    string[0] = filecontent[i + 2 * (wxFileOffset)k + 9];
                    string[1] = filecontent[i + 2 * (wxFileOffset)k + 10];
                    string[2] = '\0';
                    data = strtol(string, &stopstring, 16);

                    write_data[data_num++] = (unsigned char)data;
                }


                i += (wxFileOffset)line_len * 2;
            }
            i += 9;
        }
    }
    thread_handler->MyGauge->SetValue(100);
        
    
    if (father_semaphore == NULL)
    {
        thread_handler->Output_file->Write(write_data, data_num);

        thread_number = thread_array.GetCount();
        while (thread_number--)
        {
            thread_array[thread_number]->my_semaphore->Post();
            son_semaphore->Wait();
        }

        wxLogDebug("all complete");
        wxLogMessage("the file is convert completely");

        thread_handler->Output_file->Close();

        delete(filecontent);
        delete(son_semaphore);
    }
    else
    {
        my_semaphore->Wait();
        thread_handler->Output_file->Write(write_data, data_num);
        thread_number = thread_array.GetCount();
        while (thread_number--)
        {
            thread_array[thread_number]->my_semaphore->Post();
            son_semaphore->Wait();
        }
        father_semaphore->Post();
    }


    delete []write_data;
    wxLogDebug("complete");


     return (wxThread::ExitCode)1;
}
